/**
 * Copyright 2011 Alex Vikulov (vikuloff@gmail.com)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

package org.stockchart;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.stockchart.core.Area;
import org.stockchart.core.Axis;
import org.stockchart.core.AxisRange;
import org.stockchart.core.ChartElement;
import org.stockchart.core.SeriesPaintInfo;
import org.stockchart.indicators.AbstractIndicator;
import org.stockchart.indicators.BollingerBandsIndicator;
import org.stockchart.indicators.EmaIndicator;
import org.stockchart.indicators.EnvelopesIndicator;
import org.stockchart.indicators.MacdIndicator;
import org.stockchart.indicators.RsiIndicator;
import org.stockchart.indicators.SmaIndicator;
import org.stockchart.indicators.StochasticIndicator;
import org.stockchart.points.AbstractPoint;
import org.stockchart.series.AbstractSeries;
import org.stockchart.series.BarSeries;
import org.stockchart.series.LinearSeries;
import org.stockchart.series.SeriesBase;
import org.stockchart.utils.Tuple;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class StockChartView extends View
{
	private enum TouchMode
	{
		NONE,
		ZOOM,
		DRAG
	}
	

	public class HitTestInfo
	{
		public ChartElement element;		
		public final TreeMap<String,AbstractPoint> points = new TreeMap<String,AbstractPoint>();
	}
	
	public interface ITouchEventListener
	{
		public void onTouchEvent(MotionEvent e);
	}
	
	
	private class AreaPositionerIterator
	{
		private int index = 0;
		private float y = 0;
		private final float pxAutoHeight;
		private final float pxWidth;
		private final float pxHeight;
		
		public AreaPositionerIterator()
		{
			pxAutoHeight =  StockChartView.this.getAreaAutoHeightInPixels();
			pxWidth = StockChartView.this.getWidth();
			pxHeight = StockChartView.this.getHeight();
		}
		
		public boolean hasNext()
		{
			return index < StockChartView.this.fAreas.size();
		}
		
		public Tuple<Area,RectF> getNext()
		{
			if(!hasNext()) return null;
			
			Area a = StockChartView.this.fAreas.get(index);
			
			float height = a.isAutoHeight()? pxAutoHeight : a.getHeightInPercents() * pxHeight;
			
			Tuple<Area,RectF> result = new Tuple<Area,RectF>(a,new RectF(0,y,pxWidth,y+height));
			
			index++;
			y+=height;
			
			return result;
		}
	}
	
	// temporary appearance
	private int fClearColor = Color.WHITE;
	
	private final ArrayList<Area> fAreas = new ArrayList<Area>();
	
	private final ArrayList<AbstractIndicator> fIndicators = new ArrayList<AbstractIndicator>();
	
	private final EnumMap<Axis.Side, AxisRange> fGlobalRanges = new EnumMap<Axis.Side, AxisRange>(Axis.Side.class);

	private final Paint fPaint = new Paint();
	
	private TouchMode fMode = TouchMode.NONE;
	
	private boolean fTouchEventHandled = false;
	
	private float fOldDist = 1;
	private final PointF fDragStartPoint = new PointF();
			
	private ITouchEventListener fTouchEventUpListener;
	
	public StockChartView(Context context)
	{
		super(context);
	
		this.setFocusable(true);
	}
	
	public StockChartView(Context context, AttributeSet attrs) {
	    this(context, attrs, 0);
	    this.setFocusable(true);
	}

	public StockChartView(Context context, AttributeSet attrs, int defStyle) {
	    super(context, attrs, defStyle);
	    this.setFocusable(true);
	}
	
	public ITouchEventListener getTouchEventUpListener()
	{
		return fTouchEventUpListener;
	}
	
	public void setTouchEventUpListener(ITouchEventListener ev)
	{
		fTouchEventUpListener = ev;
	}
	
	public ArrayList<Area> getAreas()
	{		
		return fAreas;
	}
	
	public ArrayList<AbstractIndicator> getIndicators()
	{
		return fIndicators;
	}
	
	public void setClearColor(int color)
	{
		fClearColor = color;
	}
	
	public int getClearColor()
	{
		return fClearColor;
	}
	
	public void reset()
	{
		fAreas.clear();
		fIndicators.clear();
		fGlobalRanges.clear();
	}
	
	public SeriesBase findSeriesByName(String name)	
	{
		for(Area a:fAreas)
		{
			SeriesBase s = a.findSeriesByName(name);
			if(null != s)
				return s;
		}
		
		return null;
	}
	
	public Area findAreaByName(String name)
	{
		for(Area a:fAreas)
		{
			if(a.getName().equals(name)) return a;
		}
		
		return null;
	}
	
	public void load(String s) throws JSONException
	{
		reset();
		
		JSONObject j = new JSONObject(s);
		if(j.has("areas"))
		{
			JSONArray areas = j.getJSONArray("areas");
			for(int i=0;i<areas.length();i++)
			{
				JSONObject area = areas.getJSONObject(i);
				
				Area a = new Area();
				a.fromJSONObject(area);
				
				fAreas.add(a);
			}
		}
		
		if(j.has("globalAxisRanges"))
		{
			JSONArray ranges = j.getJSONArray("globalAxisRanges");
			for(int i=0;i<ranges.length();i++)
			{
				JSONObject range = ranges.getJSONObject(i);
				AxisRange ar = new AxisRange();
				Axis.Side side = Axis.Side.valueOf(range.getString("side"));
				ar.fromJSONObject(range.getJSONObject("axisRange"));
				
				fGlobalRanges.put(side, ar);
			}
		}
		
		if(j.has("indicators"))
		{
			JSONArray indicators = j.getJSONArray("indicators");
			for(int i=0;i<indicators.length();i++)
			{
				JSONObject ind = indicators.getJSONObject(i);
				
				fIndicators.add(this.jsonObjectToIndicator(ind));
			}
		}
		
		fClearColor = j.getInt("clearColor");
	}
	
	public String save() throws JSONException
	{
		JSONObject view = new JSONObject();
		JSONArray areas = new JSONArray();
		
		for(Area a: fAreas)
		{									
			areas.put(a.toJSONObject());
		}
		
		view.put("areas", areas);
		
		JSONArray ranges = new JSONArray();
		Iterator<Entry<Axis.Side, AxisRange>> iterator = this.fGlobalRanges.entrySet().iterator();

		while(iterator.hasNext())
		{
			JSONObject range = new JSONObject();
			
			Entry<Axis.Side, AxisRange> i = iterator.next();
			range.put("side", i.getKey());
			range.put("axisRange", i.getValue().toJSONObject());
			
			ranges.put(range);
		}
		
		view.put("globalAxisRanges", ranges);
		
		JSONArray indicators = new JSONArray();
		for(AbstractIndicator a:fIndicators)		
		{
			indicators.put(this.indicatorToJSONObject(a));
		}
		
		view.put("indicators", indicators);
		
		view.put("clearColor", fClearColor);
		
		return view.toString();
	}
	

	public void recalcIndicators()
	{
		for(AbstractIndicator a: fIndicators)
		{
			a.recalc();
		}
	}
	
	public AxisRange getGlobalAxisRange(Axis.Side side)
	{
		return fGlobalRanges.get(side);
	}
	
	public void disableGlobalAxisRange(Axis.Side side)
	{
		fGlobalRanges.remove(side);
	}
	
	public void enableGlobalAxisRange(Axis.Side side, AxisRange value)
	{
		fGlobalRanges.put(side, value);
	}
	
	
	public void resetView()
	{
		Set<Entry<Axis.Side, AxisRange>> entrySet = fGlobalRanges.entrySet();
		Iterator<Entry<Axis.Side, AxisRange>> iterator = entrySet.iterator();

		// calc global zoom first
		while(iterator.hasNext())
		{
			Entry<Axis.Side, AxisRange> i = iterator.next();
			i.getValue().resetViewValues();
		}
		
		for(Area a: fAreas)
		{			 
			for(Axis axis:a.getAxes())
			{
				axis.getAxisRange().resetViewValues();
			}
		}
	}
	
	public void zoom(float hFactor, float vFactor)
	{
		Set<Entry<Axis.Side, AxisRange>> entrySet = this.fGlobalRanges.entrySet();
		Iterator<Entry<Axis.Side, AxisRange>> iterator = entrySet.iterator();

		// calc global zoom first
		while(iterator.hasNext())
		{
			Entry<Axis.Side, AxisRange> i = iterator.next();
			
			if(Axis.isHorizontal(i.getKey()))
				i.getValue().zoomViewValues(hFactor);
			else if(Axis.isVertical(i.getKey()))
				i.getValue().zoomViewValues(vFactor);
		}
		
		
		for(Area a: fAreas)
		{			 
			Axis[] axes = a.getAxes();
			
			for(Axis axis:axes)
			{
				if(!fGlobalRanges.containsKey(axis.getSide()))
				{
					if(axis.isHorizontal())
						axis.getAxisRange().zoomViewValues(hFactor);
					else if(axis.isVertical())
						axis.getAxisRange().zoomViewValues(vFactor);
				}
			}
		}
	}
	
	public void move(float hFactor, float vFactor)
	{
		Set<Entry<Axis.Side, AxisRange>> entrySet = this.fGlobalRanges.entrySet();
		Iterator<Entry<Axis.Side, AxisRange>> iterator = entrySet.iterator();

		// calc global zoom first
		while(iterator.hasNext())
		{
			Entry<Axis.Side, AxisRange> i = iterator.next();
			
			if(Axis.isHorizontal(i.getKey()))
				i.getValue().moveViewValues(hFactor);
			else if(Axis.isVertical(i.getKey()))
				i.getValue().moveViewValues(vFactor);
		}
		
		
		for(Area a: fAreas)
		{			 
			Axis[] axes = a.getAxes();
			
			for(Axis axis:axes)
			{
				if(!fGlobalRanges.containsKey(axis.getSide()))
				{
					if(axis.isHorizontal())
						axis.getAxisRange().moveViewValues(hFactor);
					else if(axis.isVertical())
						axis.getAxisRange().moveViewValues(vFactor);
				}
			}
		}
	}
	
	protected void onDraw(Canvas canvas) 
	{	
		super.onDraw(canvas);

		recalc();
		resetPositions();
		
		drawClear(canvas);
		drawAreas(canvas);
	}
	
	public boolean onTouchEvent(MotionEvent ev)
	{
		int e = ev.getAction() & MotionEvent.ACTION_MASK;
		
		switch (e) 
		{
			case MotionEvent.ACTION_DOWN:
			{				
				 fMode = TouchMode.DRAG;
				 fDragStartPoint.set(ev.getX(), ev.getY());					 
			}
			break;
			case MotionEvent.ACTION_POINTER_DOWN:
			{						 
				 fOldDist = getSpacing(ev);
				 
	             if (fOldDist > 10)           	 	            	 
	            	 fMode = TouchMode.ZOOM;	            	
			}		
            break;
			case MotionEvent.ACTION_UP:
		 	case MotionEvent.ACTION_POINTER_UP:
		 	{
		 		if(e == MotionEvent.ACTION_UP)
		 		{
		 			if(!fTouchEventHandled)
		 				this.onTouchEventUp(ev);
			 		
		 			fTouchEventHandled = false;
		 		}
		 		
		 		fMode = TouchMode.NONE;
		 	}
            break;
		 	case MotionEvent.ACTION_MOVE:
		 	{		 				 	

	 			if(TouchMode.ZOOM == fMode)
	 			{	 				
	 				float newDist = getSpacing(ev);		 				
	 									  
	 				if(newDist > 10f)
	 				{
	 					float scale = newDist - fOldDist;

	 					this.zoom(inPercentsOfWidth(scale), inPercentsOfHeight(scale));
	 					
	 					invalidate();
	 					fTouchEventHandled = true;
	 				}
	 				
	 			}
	 			else if(TouchMode.DRAG == fMode)
	 			{
	 				float dx = fDragStartPoint.x - ev.getX();
	 				float dy = ev.getY() - fDragStartPoint.y;
	 				
	 				if(dx != 0f || dy != 0f)
	 				{
	 					this.move(inPercentsOfWidth(dx), inPercentsOfHeight(dy));
	 				
	 					invalidate();
	 					fTouchEventHandled = true;
	 				}
	 			}
		 	}
		 	break;
		 		
		}

		return true;
	}
	
	private void onTouchEventUp(MotionEvent ev)
	{
		if(null != this.fTouchEventUpListener)
			this.fTouchEventUpListener.onTouchEvent(ev);
	}

	private JSONObject indicatorToJSONObject(AbstractIndicator a) throws JSONException
	{
		JSONObject obj = new JSONObject();
		obj.put("valueIndex", a.getValueIndex());
		obj.put("src", a.getSrc().getName());
		
		if(a instanceof EmaIndicator)
		{
			EmaIndicator ema = (EmaIndicator)a;
			obj.put("type", "ema");
			obj.put("periodsCount", ema.getPeriodsCount());
			obj.put("dst", ema.getDst().getName());
		}
		else if(a instanceof SmaIndicator)
		{
			SmaIndicator sma = (SmaIndicator)a;
			obj.put("type", "sma");
			obj.put("periodsCount", sma.getPeriodsCount());
			obj.put("dst", sma.getDst().getName());
		}
		else if(a instanceof RsiIndicator)
		{
			RsiIndicator rsi = (RsiIndicator)a;
			obj.put("type", "rsi");
			obj.put("periodsCount", rsi.getPeriodsCount());
			obj.put("dst", rsi.getDst().getName());
		}
		else if(a instanceof EnvelopesIndicator)
		{
			EnvelopesIndicator env = (EnvelopesIndicator)a;
			obj.put("type", "envelopes");
			obj.put("periodsCount", env.getPeriodsCount());
			obj.put("dstUpperEnvelope", env.getDstUpperEnvelope().getName());
			obj.put("dstLowerEnvelope", env.getDstLowerEnvelope().getName());
			obj.put("percent", env.getPercent());
		}
		else if(a instanceof MacdIndicator)
		{
			MacdIndicator macd = (MacdIndicator)a;
			obj.put("type", "macd");
			obj.put("longMacdPeriod", macd.getLongMacdPeriod());
			obj.put("shortMacdPeriod", macd.getShortMacdPeriod());
			obj.put("signalMacdPeriod", macd.getSignalPeriod());
			obj.put("dstMacd", macd.getDstMacd().getName());
			obj.put("dstSignal", macd.getDstSignal().getName());
			obj.put("dstHistogram", macd.getDstHistogram().getName());	
		}
		else if(a instanceof BollingerBandsIndicator)
		{
			BollingerBandsIndicator bb = (BollingerBandsIndicator)a;
			obj.put("type", "bb");
			obj.put("periodsCount", bb.getPeriodsCount());
			obj.put("lowerCoeff", bb.getLowerCoeff());
			obj.put("upperCoeff", bb.getUpperCoeff());
			obj.put("dstUpper", bb.getDstUpperBand().getName());
			obj.put("dstLower", bb.getDstLowerBand().getName());
			obj.put("dstSma", bb.getDstSMA().getName());
		}
		else if(a instanceof StochasticIndicator)
		{
			StochasticIndicator s = (StochasticIndicator)a;
			obj.put("type", "stoch");
			obj.put("periodsCount", s.getPeriodsCount());
			obj.put("slowD", s.getSlowD());
			obj.put("slowK", s.getSlowK());
			obj.put("dstSlowD", s.getDstSlowD().getName());
			obj.put("dstSlowK", s.getDstSlowK().getName());
		}
		
		return obj;

	}
	
	private AbstractIndicator jsonObjectToIndicator(JSONObject j) throws JSONException
	{
		String type = j.getString("type");
		int valueIndex = j.getInt("valueIndex");
		SeriesBase src = findSeriesByName(j.getString("src"));
		
		AbstractIndicator result = null;
		
		if(type.equals("sma"))
		{
			LinearSeries dst = (LinearSeries)findSeriesByName(j.getString("dst"));
			int pk = j.getInt("periodsCount");
			
			SmaIndicator s = new SmaIndicator(src,valueIndex,dst);
			s.setPeriodsCount(pk);
			result = s;
		}
		else if(type.equals("ema"))
		{
			LinearSeries dst = (LinearSeries)findSeriesByName(j.getString("dst"));
			int pk = j.getInt("periodsCount");
			
			EmaIndicator e = new EmaIndicator(src,valueIndex,dst);
			e.setPeriodsCount(pk);
			result = e;
		}
		else if(type.equals("rsi"))
		{
			LinearSeries dst = (LinearSeries)findSeriesByName(j.getString("dst"));
			int pk = j.getInt("periodsCount");
			
			RsiIndicator rsi = new RsiIndicator(src,valueIndex,dst); 
			rsi.setPeriodsCount(pk);
			result = rsi;
		}
		else if(type.equals("envelopes"))		
		{
			LinearSeries upper = (LinearSeries)findSeriesByName(j.getString("dstUpperEnvelope"));
			LinearSeries lower = (LinearSeries)findSeriesByName(j.getString("dstLowerEnvelope"));
			
			int pk = j.getInt("periodsCount");
			double percent = j.getDouble("percent");
			
			EnvelopesIndicator env = new EnvelopesIndicator(src,valueIndex,upper,lower);
			env.setPeriodsCount(pk);
			env.setPercent(percent);
			result = env;
		}
		else if(type.equals("macd"))
		{
			LinearSeries macd = (LinearSeries)findSeriesByName(j.getString("dstMacd"));
			LinearSeries signal = (LinearSeries)findSeriesByName(j.getString("dstSignal"));
			BarSeries bar = (BarSeries)findSeriesByName(j.getString("dstHistogram"));
			int longPeriod = j.getInt("longMacdPeriod");
			int shortPeriod = j.getInt("shortMacdPeriod");
			int signalPeriod = j.getInt("signalMacdPeriod");
			
			MacdIndicator m = new MacdIndicator(src,valueIndex,macd,signal,bar);
			m.setLongMacdPeriod(longPeriod);
			m.setShortMacdPeriod(shortPeriod);
			m.setSignalPeriod(signalPeriod);
			
			result = m;
		}
		else if(type.equals("bb"))
		{
			LinearSeries sma = (LinearSeries)findSeriesByName(j.getString("dstSma"));
			LinearSeries upper = (LinearSeries)findSeriesByName(j.getString("dstUpper"));
			LinearSeries lower = (LinearSeries)findSeriesByName(j.getString("dstLower"));
			
			int periodsCount = j.getInt("periodsCount");
			double lowerCoeff = j.getDouble("lowerCoeff");
			double upperCoeff = j.getDouble("upperCoeff");
						
			BollingerBandsIndicator bb = new BollingerBandsIndicator(src,valueIndex,sma,upper,lower);
			bb.setPeriodsCount(periodsCount);
			bb.setLowerCoeff(lowerCoeff);
			bb.setUpperCoeff(upperCoeff);
			result = bb;
		}
		else if(type.equals("stoch"))
		{
			LinearSeries d = (LinearSeries)findSeriesByName(j.getString("dstSlowD"));
			LinearSeries k = (LinearSeries)findSeriesByName(j.getString("dstSlowK"));
			
			int periodsCount = j.getInt("periodsCount");
			int sd = j.getInt("slowD");
			int sk = j.getInt("slowK");
			
			StochasticIndicator s = new StochasticIndicator(src,valueIndex,d,k);
			s.setPeriodsCount(periodsCount);
			s.setSlowD(sd);
			s.setSlowK(sk);
			
			result = s;
		}

		return result;
	}
	
	
	private float inPercentsOfHeight(float value)
	{
		return value / (float)this.getHeight();
	}
	
	private float inPercentsOfWidth(float value)
	{
		return value / (float)this.getWidth();
	}
	
	private void calcGlobalValues()
	{
		Set<Entry<Axis.Side, AxisRange>> entrySet = this.fGlobalRanges.entrySet();
		Iterator<Entry<Axis.Side, AxisRange>> iterator = entrySet.iterator();

		while(iterator.hasNext())
		{
			Entry<Axis.Side, AxisRange> i = iterator.next();
			
			AxisRange dst = i.getValue();
			dst.resetAutoValues();
			
			if(dst.isAuto())
			{
				for(Area a : fAreas)
				{
					Axis axis = a.getAxis(i.getKey());	
					AxisRange src = axis.getAxisRange();
					
					dst.expandAutoValues(src.getMaxOrAutoValue(), src.getMinOrAutoValue());
				}
			}
		}
		
		for(Area a:fAreas)
		{
			Axis[] axes = a.getAxes();
			
			for(Axis axis:axes)
			{
				axis.setGlobalAxisRange(fGlobalRanges.get(axis.getSide()));
			}
		}	
	}
	
	public void recalc()
	{
		// set local maxmin x and y
		for(Area a:fAreas)
		{
			a.calcAutoValues();
		}

		// set globals
		calcGlobalValues();
		
		// set y again if global x changed 
		for(Area a:fAreas)
		{
			a.calcYAutoValues();
		}
	}
	
	public HitTestInfo getHitTestInfo(float x, float y)
	{
		HitTestInfo info = new HitTestInfo();
		
		resetPositions();
		
		for(Area a : fAreas)
		{
			if(a.getAbsoluteBounds().contains(x, y))
			{				
				if(a.getPlot().getAbsoluteBounds().contains(x,y))
				{					
					info.element = a.getPlot();
					SeriesPaintInfo pinfo = new SeriesPaintInfo();
					RectF rectF = new RectF();
					
					for(SeriesBase s: a.getSeries())
					{				
						AbstractSeries<?> as = (AbstractSeries<?>)s;
						
						pinfo.loadFromRange(a.getAxis(as.getXAxisSide()).getAxisRangeOrGlobalAxisRange(),
											a.getAxis(as.getYAxisSide()).getAxisRangeOrGlobalAxisRange(), 
											a.getPlot().getBounds().width(), 
											a.getPlot().getBounds().height());
						
						if(!as.isVisibleOnScreen(pinfo.MaxX, pinfo.MinX)) continue;
						
						int index = as.convertToArrayIndexZeroBased(pinfo.MinX);
						
						for(int i=index;i<as.getPointCount();i++)
						{							
							AbstractPoint ap = as.getPointAt(i);		
							
							float scaleIndex = as.convertToScaleIndex(i);
							
							if(ap.isVisible())
							{							
								double[] maxMin = ap.getMaxMin();
								float x1 = pinfo.getX(scaleIndex - 0.5f) + 1f;
								float x2 = pinfo.getX(scaleIndex + 0.5f) - 1f;

								float max = pinfo.getY(maxMin[0]);
								float min = pinfo.getY(maxMin[1]);
								
								if(maxMin[0] == maxMin[1])
								{
									float d = (x2-x1)/2f; 
									max -= d;
									min += d;
								}
									
								
								PointF p = a.getPlot().getAbsolutePosition(x1,max);								
								rectF.set(p.x, p.y, p.x + (x2-x1), p.y + (min-max));
								
								if(rectF.contains(x, y))
								{
									info.points.put(s.getName(), ap);
									break;
								}
									
							}
							
							if(scaleIndex > pinfo.MaxX)
								break;
						}
					}
						
				}
				else
				{
					for(Axis axis : a.getAxes())
					{
						if(axis.getAbsoluteBounds().contains(x,y))
						{
							info.element = axis;
							break;
						}
					}
				}
				
				if(info.element == null)
					info.element = a;
				
				break;
			}
		}
		
		return info;
	}
	
	public void resetPositions()
	{
		AreaPositionerIterator i = new AreaPositionerIterator();
		
		final float[] lr = getLeftRightMargins();
		
		while(i.hasNext())
		{
			Tuple<Area,RectF> t = i.getNext();
			Area a = t.x;
			RectF rect = t.y;
//			RectF m = a.getSideMargins();
//			
//			float leftMargin = lr[0] - m.left;
//			float rightMargin =lr[1] - m.right;	
//			
//			float width = rect.width()-rightMargin - leftMargin;
			a.setGlobalMargins(lr[0], lr[1]);
			
			a.setBounds(0f, rect.top, rect.width(), rect.height());
		}
	}

	private void drawAreas(Canvas c)
	{		
		for(Area a: fAreas)
			a.draw(c);
	}

	
	private void drawClear(Canvas c)
	{
		fPaint.setColor(fClearColor);
		c.drawRect(0,0,getWidth(),getHeight(),fPaint);
	}
			

	private float[] getLeftRightMargins()
	{
		float leftMargin = Float.MIN_VALUE;
		float rightMargin = Float.MIN_VALUE;
		
		for(Area a: fAreas)
		{
			RectF r = a.getSideMargins();
			
			if(r.left > leftMargin)
				leftMargin = r.left;
			
			if(r.right > rightMargin)
				rightMargin =r.right;
		}
		
		return new float[] { leftMargin, rightMargin };
	}
	
	private float getAreaAutoHeightInPixels()
	{
		int autoAreaCount = 0;
		
		final float totalHeight = this.getHeight();
		
		float remainingHeight = totalHeight;
		
		for(Area a: fAreas)
		{
			if(a.isAutoHeight())
				autoAreaCount++;
			else
				remainingHeight-=a.getHeightInPercents() * totalHeight;
		}			

		if(0 == autoAreaCount)
			return Float.NaN;
		
		if(remainingHeight < 0)
			return 0f;
		
		return (float)remainingHeight/(float)autoAreaCount; 
	}
		
	private static float getSpacing(MotionEvent event) 
	{
		   float x = event.getX(0) - event.getX(1);
		   float y = event.getY(0) - event.getY(1);
		   
		   return (float)android.util.FloatMath.sqrt(x * x + y * y);
	}
}
